﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using WCS.Infrastructure.Dto.Request.Log;
using WCS.Infrastructure.Dto.Request.Payment;
using WCS.Infrastructure.Dto.Response.Common;
using WCS.Service.Interface;

namespace WCS.Service.Instance
{
    /// <summary>
    /// 定时后台服务
    /// </summary>
    public class AtRegularTimeBackgroundService : BackgroundService
    {
        private readonly ILogger<AtRegularTimeBackgroundService> _logger;
        private readonly IServiceProvider _serviceProvider;

        public AtRegularTimeBackgroundService(
            ILogger<AtRegularTimeBackgroundService> logger,
            IServiceProvider serviceProvider)
        {
            _logger = logger;
            _serviceProvider = serviceProvider;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            // 以固定计划时间为基准推进，避免因长跑导致跳过次日
            var nextRun = GetNextRunTime(DateTime.Now);

            while (!stoppingToken.IsCancellationRequested)
            {
                try
                {
                    var now = DateTime.Now;

                    // 正常等待到计划时间（如果已过计划时间则不等待，直接补跑）
                    if (now < nextRun)
                    {
                        _logger.LogInformation($"下次结算任务执行时间: {nextRun:yyyy-MM-dd HH:mm:ss}");
                        await Task.Delay(nextRun - now, stoppingToken);
                    }

                    if (stoppingToken.IsCancellationRequested)
                    {
                        break;
                    }

                    // 执行一次（对应当前计划的 1 点）
                    await ExecuteSettlementTask(null);

                    // 推进到下一天的计划时间
                    nextRun = nextRun.AddDays(1);

                    // 补偿执行：如果当前时间已经超过下一次计划时间，立即补跑，直至追上
                    while (DateTime.Now >= nextRun && !stoppingToken.IsCancellationRequested)
                    {
                        _logger.LogWarning($"错过计划时间 {nextRun:yyyy-MM-dd HH:mm:ss}，立即补跑一次");
                        await ExecuteSettlementTask(null);
                        nextRun = nextRun.AddDays(1);
                    }
                }
                catch (OperationCanceledException)
                {
                    break;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "定时结算任务执行异常");
                    await Task.Delay(TimeSpan.FromHours(1), stoppingToken);
                }
            }
        }


        public async Task ExecuteSettlementTask(DateTime? dateTime)
        {
            _logger.LogInformation($"开始执行定时结算任务: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");

            await using var scope = _serviceProvider.CreateAsyncScope();
            var paymentService = scope.ServiceProvider.GetRequiredService<IPaymentService>();
            var dividedService = scope.ServiceProvider.GetRequiredService<IDividedService>();
            var logService = scope.ServiceProvider.GetRequiredService<ILogService>();

            // 1) 结算
            try
            {
                var result = await paymentService.AddSettlementRecordAsync(dateTime);
                if (result.code == ApiCode.ok)
                {
                    _logger.LogInformation("定时结算任务执行成功");
                    await logService.AddActionLogAsync(new ActionLogRequestDto
                    {
                        ApiUrl = "AtRegularTime.Settlement",
                        Type = "定时任务",
                        Action = "结算",
                        Params = "",
                        Result = 0,
                        ResultMessage = "定时结算任务执行成功",
                        UserId = ""
                    });
                }
                else if (result.code == ApiCode.fail)
                {
                    _logger.LogWarning($"定时结算任务执行失败: {result.message}");
                    await logService.AddErrorLogAsync(new ErrorLogRequestDto
                    {
                        Api = "AtRegularTime.Settlement",
                        Message = $"定时结算任务执行失败: {result.message}",
                        Params = "",
                        UserId = ""
                    });
                    return;
                }
                else
                {
                    _logger.LogWarning($"定时结算任务超时");
                    await logService.AddErrorLogAsync(new ErrorLogRequestDto
                    {
                        Api = "AtRegularTime.Settlement",
                        Message = "定时结算任务超时",
                        Params = "",
                        UserId = ""
                    });
                    return;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "执行结算任务时发生异常");
                await logService.AddErrorLogAsync(new ErrorLogRequestDto
                {
                    Api = "AtRegularTime.Settlement",
                    Message = $"执行结算任务时发生异常：{ex.Message}",
                    Params = "",
                    UserId = ""
                });
                return;
            }

            // 2) 分账
            try
            {
                var result1 = await dividedService.AddDividedRecordAsync();
                if (result1.code == ApiCode.ok)
                {
                    _logger.LogInformation("定时分账任务执行成功");
                    await logService.AddActionLogAsync(new ActionLogRequestDto
                    {
                        ApiUrl = "AtRegularTime.Divided",
                        Type = "定时任务",
                        Action = "分账",
                        Params = "",
                        Result = 0,
                        ResultMessage = "定时分账任务执行成功",
                        UserId = ""
                    });
                }
                else if (result1.code == ApiCode.fail)
                {
                    _logger.LogWarning($"定时分账任务执行失败: {result1.message}");
                    await logService.AddErrorLogAsync(new ErrorLogRequestDto
                    {
                        Api = "AtRegularTime.Divided",
                        Message = $"定时分账任务执行失败: {result1.message}",
                        Params = "",
                        UserId = ""
                    });
                    return;
                }
                else
                {
                    _logger.LogWarning($"定时分账任务超时");
                    await logService.AddErrorLogAsync(new ErrorLogRequestDto
                    {
                        Api = "AtRegularTime.Divided",
                        Message = "定时分账任务超时",
                        Params = "",
                        UserId = ""
                    });
                    return;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "执行分账任务时发生异常");
                await logService.AddErrorLogAsync(new ErrorLogRequestDto
                {
                    Api = "AtRegularTime.Divided",
                    Message = $"执行分账任务时发生异常：{ex.Message}",
                    Params = "",
                    UserId = ""
                });
                return;
            }

            // 3) 分账结算
            try
            {
                var result2 = await dividedService.UpdateDividedRecordAsync(dateTime);
                if (result2.code == ApiCode.ok)
                {
                    _logger.LogInformation("定时分账结算任务执行成功");
                    await logService.AddActionLogAsync(new ActionLogRequestDto
                    {
                        ApiUrl = "AtRegularTime.DividedSettlement",
                        Type = "定时任务",
                        Action = "分账结算",
                        Params = "",
                        Result = 0,
                        ResultMessage = "定时分账结算任务执行成功",
                        UserId = ""
                    });
                }
                else if (result2.code == ApiCode.fail)
                {
                    _logger.LogWarning($"定时分账结算任务执行失败: {result2.message}");
                    await logService.AddErrorLogAsync(new ErrorLogRequestDto
                    {
                        Api = "AtRegularTime.DividedSettlement",
                        Message = $"定时分账结算任务执行失败: {result2.message}",
                        Params = "",
                        UserId = ""
                    });
                    return;
                }
                else
                {
                    _logger.LogWarning($"定时分账结算任务超时");
                    await logService.AddErrorLogAsync(new ErrorLogRequestDto
                    {
                        Api = "AtRegularTime.DividedSettlement",
                        Message = "定时分账结算任务超时",
                        Params = "",
                        UserId = ""
                    });

                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "执行分账结算任务时发生异常");
                await logService.AddErrorLogAsync(new ErrorLogRequestDto
                {
                    Api = "AtRegularTime.DividedSettlement",
                    Message = $"执行分账结算任务时发生异常：{ex.Message}",
                    Params = "",
                    UserId = ""
                });
                return;
            }

            _logger.LogInformation($"定时任务全部执行完成: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
        }

        private DateTime GetNextRunTime(DateTime currentTime)
        {
            // 计算下次凌晨1点的时间
            var nextRun = currentTime.Date.AddHours(1); // 今天凌晨1点

            // 如果当前时间已经过了今天凌晨1点，则设置为明天凌晨1点
            if (currentTime >= nextRun)
            {
                nextRun = nextRun.AddDays(1);
            }

            return nextRun;
        }
    }
}